package com.fingard.dsp.bank.directbank.allinpay03;

import com.alibaba.fastjson.JSONObject;
import com.fingard.dsp.bank.directConfig.ActSetItem;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.allinpay03.util.AESUtil;
import com.fingard.dsp.bank.directbank.allinpay03.util.CryptoUtil;
import com.fingard.dsp.bank.reqrespobj.batpayrec.ReqBatPayRec;
import com.fingard.text.StringHelper;
import sun.misc.BASE64Decoder;

import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.fingard.dsp.bank.directbank.allinpay03.util.AESUtil.getBaseFromRandKey;

public class AllInPay03Base extends DirectBase {
    protected String getCharset() {
        return getBankConfig().getCharset("utf-8");
    }
    protected void bizContentPut(Map<String, Object> bizContent, String key, Object value) {
        if (bizContent != null && key != null && value != null) {
            bizContent.put(key, value);
        }
    }
    protected String doSendData(String service, String method, Map<String, Object> bizContent, ActSetItem actItem) throws IOException {
        String appId = actItem.appId;
        String mchtId = actItem.merchantId;
        String timestamp = getCurrentTimestamp();
        String version = "2.0";
        String signType = "4";
        String jrlNo = UUID.randomUUID().toString();

        String bizContentStr = new JSONObject(bizContent).toJSONString();

        Map<String, Object> postData = new HashMap<String, Object>();
        postData.put("appId", appId);
        postData.put("mchtId", mchtId);
        postData.put("timestamp", timestamp);
        postData.put("version", version);
        postData.put("service", service);
        postData.put("method", method);
        postData.put("signType", signType);
        postData.put("jrlNo", jrlNo);
        postData.put("bizContent", bizContentStr);

        String source = getSourceString(postData);
        String sign = CryptoUtil.doSignBySHA1withRSA(source, actItem.ownKeyStorePath);
        postData.put("sign", sign);
        JSONObject json = new JSONObject(postData);

        // 请求参数
        String params = json.toString();
        WriteBankLogLn("2.提交银行的地址：" + bankFront.serverURL);
        WriteBankLogStep2(params);
        // 响应数据
        String response = doPost(bankFront.serverURL, params);
        WriteBankLogStep3(response);
        return response;
    }
    public String doPost(String serverUrl, String params) {
        HttpURLConnection connection = null;
        InputStream is = null;
        OutputStream os = null;
        BufferedReader br = null;
        String result = null;

        try {
            URL url = new URL(serverUrl);
            //使用代理
            boolean enableProxy = getBankConfig().getValueAsBool("enableProxy");
            if(enableProxy){
                String proxyIP = getBankConfig().getValueAsString("proxyIP");
                int proxyPort = Integer.parseInt(getBankConfig().getValueAsString("proxyPort"));
                //HttpHost proxy = new HttpHost(proxyIP,proxyPort,"http");
                Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyIP, proxyPort));
                connection = (HttpURLConnection) url.openConnection(proxy);
            }else {
                connection = (HttpURLConnection) url.openConnection();
            }

            // 设置请求方式
            connection.setRequestMethod("POST");

            // 设置连接属性
            connection.setConnectTimeout(15000);
            connection.setReadTimeout(60000);
            connection.setDoOutput(true);
            connection.setDoInput(true);

            // 设置Http请求头信息
            connection.setRequestProperty("Accept", "application/json, text/plain, */*");
            connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");

            // 发送请求
            os = connection.getOutputStream();
            os.write(params.getBytes("utf-8"));

            // 获取请求响应
            is = connection.getInputStream();
            br = new BufferedReader(new InputStreamReader(is, "UTF-8"));

            StringBuffer sbf = new StringBuffer();
            String temp = null;

            while ((temp = br.readLine()) != null) {
                sbf.append(temp);
                sbf.append("\r\n");
            }

            result = sbf.toString();

        } catch (MalformedURLException e) {
            e.printStackTrace();

        } catch (IOException e) {
            e.printStackTrace();

        }finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            connection.disconnect();
        }
        return result;
    }
    public Map prepareData(ReqBatPayRec p_req1988, String filePath, ActSetItem actItem,String remark) throws Exception {


//		String pwdRsaEnced = CryptoUtil.doEncryptByRSA(pwd, YourParams.Allinpay_PKCS8_RSA_Public_Key);
//		System.out.println("pwdRsaEnced:" + pwdRsaEnced);
//
//		String pwdAesEnced = Base64.getEncoder()
//				.encodeToString(AESUtil.encryptForH5KeyBord(pwdRsaEnced, queryPwdRandAesKey(appId, mchtId, "batchDf")));

        //String pwdAesEnced = simulateH5Board(pwd, queryPwdRandAesKey(appId, mchtId, "batchDf" ,actItem),actItem);
        //WriteBankLogLn("pwdAesEnced:" + pwdAesEnced);

        String aesKey = generateFileRandAesKey();
        WriteBankLogLn("aesKey:" + aesKey);
        String aesKeyEnced =  byte2HexStr(
                new BASE64Decoder().decodeBuffer(CryptoUtil.doEncryptByRSA(aesKey, actItem.webAPISercretKey) ) );

        WriteBankLogLn("aesKeyEnced:" + aesKeyEnced);

        WriteBankLogLn("fileCont:" + filePath);

        String fileContEned = byte2HexStr(AESUtil.encrypt(filePath, new SecretKeySpec(getBaseFromRandKey(aesKey), AESUtil.KEY_ALGORITHM)));

        WriteBankLogLn("fileContEned:" + fileContEned);

        String fileCont2 = AESUtil.decrypt(toByteArray(fileContEned), new SecretKeySpec(getBaseFromRandKey(aesKey), AESUtil.KEY_ALGORITHM));

        WriteBankLogLn("fileCont2:" + fileCont2);

        Map<String, Object> dataMap = new HashMap<String, Object>();
        dataMap.put("batchNo", actItem.merchantId+p_req1988.reqBatchNO);
        dataMap.put("custId", actItem.merchantId);
        dataMap.put("transCode", "batchDf");
        dataMap.put("fileType", "01");
        //dataMap.put("fileName", System.currentTimeMillis() + "");
        dataMap.put("transferSumCnt", p_req1988.totalNum);
        dataMap.put("transferSumAmt", StringHelper.amountMultiply100(p_req1988.totalAmt));

        dataMap.put("randomKey", aesKeyEnced);
        dataMap.put("content", fileContEned);
        //	dataMap.put("payPwd", pwdAesEnced);
        dataMap.put("remark", remark);
        //dataMap.put("serviceUrl", "");
        dataMap.put("version", "2.0");
        dataMap.put("businessCode", "6002");
        dataMap.put("transSignType", "");

        String source = getSourceString(dataMap);
        WriteBankLogLn("accountTransfer_API source : " + source);
        String transSign = CryptoUtil.doSignBySHA1withRSA(source, actItem.ownCertFilePath);
        dataMap.put("transSign", transSign);


        return dataMap;

    }

    /**
     *生成文件内容AES随机密钥
     * @return
     */
    public String generateFileRandAesKey() {
        String randPool = "0123456789";
        StringBuilder sb = new StringBuilder();
        int randInt = 0;
        int poolLen = randPool.length();
        for (int i = 0; i < 32; i++) {
            randInt = (int) (Math.random() * poolLen);
            sb.append(randPool.charAt(randInt));
        }

        String randomKey = new String(sb);
        return randomKey;
    }
    /**
     * bytes转换成十六进制字符串
     *
     * @return String 每个Byte值之间空格分隔
     */
    public static String byte2HexStr(byte[] b) {
        String stmp = "";
        StringBuilder sb = new StringBuilder("");
        for (int n = 0; n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0xFF);
            sb.append((stmp.length() == 1) ? "0" + stmp : stmp);
//            sb.append(" ");
        }
        return sb.toString().toUpperCase().trim();
    }
    private String loadContentFromFile(String filePath) throws Exception {

        /*String retStr = "X,080803000053305,3,3,3,6002,2\r\n" +
                "1111,13564610082,140423198906211220,161228176259332,1,\r\n" +
                "小小,13488880090,110101201003073458,191204550875149,1,\r\n" +
                "小猫,13488880090,11010120100307345X,191204550875149,1,测试验证发放";*/
        String retStr = null;
        if( filePath == null || "".equals(filePath)) {
            return retStr;
        }
        FileChannel fcin = null;
        try {
            StringBuilder sb = new StringBuilder();

            long start = System.currentTimeMillis();// 开始时间
            int bufSize = 1024;// 1K缓冲区
            File fin = new File(filePath);
            /*
             * 通道就是为操作文件而建立的一个连接。（读写文件、内存映射等） 此处的getChannel()可以获取通道；
             * 用FileChannel.open(filename)也可以创建一个通道。 "r"表示只读。
             *
             * RandomAccessFile是独立与I/O流家族的类，其父类是Object。 该类因为有个指针可以挪动，所以，可以从任意位置开始读取文件数据。
             **/
            fcin = new RandomAccessFile(fin, "r").getChannel();
            // 给字节缓冲区分配大小
            ByteBuffer rBuffer = ByteBuffer.allocate(bufSize);
//	        String enterStr = "\n";


            String tempString = null;
            while (fcin.read(rBuffer) != -1) {// 每次读1k到缓冲区
                int rSize = rBuffer.position();// 记录缓冲区当前位置
                rBuffer.rewind();// 位置归零，标记取消，方便下次循环重新读入缓冲区。
                byte[] bs = new byte[rSize];
                rBuffer.get(bs);// 将缓冲区数据读到字节数组中
                rBuffer.clear();// 清除缓冲
                sb.append(new String(bs));

            }
            long end = System.currentTimeMillis();// 结束时间
            WriteBankLogLn("传统IO读取数据,指定缓冲区大小，总共耗时：" + (end - start) + "ms");
            retStr = new String(sb);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fcin != null) {
                    fcin.close();
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        return retStr;
    }
    public static PrivateKey getRSAPrivateKey(String pkcs8_rsa_private_key) throws IOException {
        byte[] keyBytes = pkcs8_rsa_private_key.getBytes();

        String pem = new String(keyBytes);
        pem = pem.replace("-----BEGIN PRIVATE KEY-----", "");
        pem = pem.replace("-----END PRIVATE KEY-----", "");
        pem = pem.replace("\n", "");

        byte [] decoded = new BASE64Decoder().decodeBuffer(pem);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);

        PrivateKey privateKey = null;

        try {
            KeyFactory kf = KeyFactory.getInstance("RSA");
            privateKey = kf.generatePrivate(spec);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return privateKey;
    }
    public static byte[] toByteArray(String hexString) {
        if (hexString == null)
            return null;
        hexString = hexString.toLowerCase();
        final byte[] byteArray = new byte[hexString.length() >> 1];
        int index = 0;
        for (int i = 0; i < hexString.length(); i++) {
            if (index  > hexString.length() - 1)
                return byteArray;
            byte highDit = (byte) (Character.digit(hexString.charAt(index), 16) & 0xFF);
            byte lowDit = (byte) (Character.digit(hexString.charAt(index + 1), 16) & 0xFF);
            byteArray[i] = (byte) (highDit << 4 | lowDit);
            index += 2;
        }
        return byteArray;
    }
    public static String getCurrentTimestamp() {
        return new SimpleDateFormat("yyyyMMddhhmmss").format(new Date());
    }
    public String getSourceString(Map<String, Object> postData) {
        if (null == postData) {
            return "";
        }

        Map<String, Object> sortedData = new TreeMap<String, Object>();
        sortedData.putAll(postData);

        StringBuilder source = new StringBuilder();

        for (String key : sortedData.keySet()) {
            if (key.equals("sign") || StringHelper.isNullOrEmpty((String) sortedData.get(key))) {
                continue;
            }

            Object value = sortedData.get(key);

            if (value instanceof String) {
                source.append(key + "=" + value + "&");
            }
        }

        if (source.length() > 0) {
            source.deleteCharAt(source.length() - 1);
        }

        return source.toString();
    }
}
